#pragma once

#include <AVUIEventableObject.h>

namespace AVUI {

class Mouse;
class KeyboardDevice;
class UIElement;
class Control;
class MouseEventArgs;
class PopupControlService;
class InputDevice;

class InputEventArgs : public RoutedEventArgs
{
public:
    DECLARE_ELEMENT(InputEventArgs, RoutedEventArgs);

    InputEventArgs(InputDevice* pInputDevice) : m_pInputDevice(pInputDevice) { };

    InputDevice* get_Device() { return m_pInputDevice; }
private:
    InputDevice* m_pInputDevice;
};

class InputManager : public Object
{
    friend class KeyboardDevice;
public:
    static InputManager* get_Current();   

    InputManager();
    ~InputManager();

    void Update();

    bool MessageKeyDown(Key::Enum keyDown);
    bool MessageKeyUp(Key::Enum keyUp);
    bool MessageKeyChar(Key::Enum keyChar, WCHAR wch);
    bool MessageMouseMove(int xDelta, int yDelta, int zDelta);
    bool MessageMousePosition(int xPosition, int yPosition, int zPosition);
    bool MessageMouseButtonDown(MouseButton::Enum buttonDown);
    bool MessageMouseButtonUp(MouseButton::Enum buttonUp);

    Point get_MousePosition(UIElement* pElementRelativeTo);
    bool CaptureMouse(UIElement* pUIElementCapture);
    UIElement* get_MouseCapture();
    void ReleaseMouseCapture(UIElement* pUIElementCapture);   
    void NotifyHitTestInvalidated();

    void LockInputs() { m_isInputLocked = true; }
    void UnlockInputs() { m_isInputLocked = false; UpdateMouseOver(); }
    void UpdateMouseOver();
    void UpdateCursor();

    void PostProcessInput(MouseEventArgs* pArgs);

    Mouse* get_Mouse() { return m_pMouseDevice; }
    PopupControlService* get_PopupControlService() { return m_pPopupControlService; }

    bool ProcessInput(InputEventArgs* pInput);

    KeyboardDevice* get_PrimaryKeyboardDevice() { return m_pKeyboardDevice; }
    Object* get_MostRecentInputDevice() { return m_pMostRecentInputDevice; }

private:
    static void RaiseEvent(DependencyObject* pSource, RoutedEventArgs* pArgs);
    void HitTestInvalidatedAsyncCallback(Object* pSender, Object* pArg);

    bool m_isInputLocked;
    bool m_hasHitTestInvalidatedAsyncOperation;

    Object* m_pMostRecentInputDevice;

    Mouse* m_pMouseDevice;
    KeyboardDevice* m_pKeyboardDevice;
    PopupControlService* m_pPopupControlService;
};

};
